/**
 * 
 */
package com.sebscape.sebcms.security.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.jdo.annotations.FetchGroup;
import javax.jdo.annotations.FetchGroups;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.NotPersistent;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.persistence.CascadeType;
import javax.persistence.OneToOne;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.appengine.api.datastore.Key;
import com.sebscape.sebcms.Constants;
import com.sebscape.sebcms.persistence.domain.ICrudPermissions;
import com.sebscape.sebcms.persistence.domain.IPersistedObject;
import com.sebscape.sebcms.persistence.domain.IUpdateable;
import com.sebscape.sebcms.tools.Utils;

/**
 * @author Stephen
 *
 */
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
@FetchGroups( {@FetchGroup(name="extended_info", members={@Persistent(name="demographic"),@Persistent(name="groupKeySet"), @Persistent(name="roleKeySet")})})
public class User implements Serializable, IPersistedObject, IUpdateable<User>, ICrudPermissions {
	private static final long serialVersionUID = 3237150725708083957L;
	private static Log log = LogFactory.getLog( User.class );
	
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;
	
	@Persistent
	private String name;
	
	@Persistent
	private String description;
	
	@Persistent
	private String specialCode;
	
	@Persistent
	private String statusCode;
	
	@Persistent
	private Date creationDate;
	
	@Persistent
	private Date renewalDate;
	
	// username is stored in the superclass field "name".
	@Persistent
	private String password;
	@Persistent
	private String firstname;
	@Persistent
	private String lastname;
	@Persistent
	private String emailAddress;
	
	@OneToOne (mappedBy = "user", cascade = CascadeType.ALL)
	private Demographic demographic;

	@Persistent
	private Set<Key> groupKeySet;
	@Persistent
	private Set<Key> roleKeySet;
	@NotPersistent
	private List<Group> groups;
	@NotPersistent
	private List<Role> roles;
	
	/**
	 * default
	 * @param username stored as the 'name' attibut of the super method.
	 * @param description
	 */
	public User() {
		super();
		this.setDemographic( new Demographic() );
	}

	/**
	 * Minimal constructor
	 * @param username stored as the 'name' attibut of the super method.
	 * @param description
	 */
	public User(String username, String description) {
		this();
		this.setName( username );
		this.setDescription( description );
	}
	
	/**
	 * The full constructor.
	 * @param name
	 * @param description
	 * @param password
	 * @param firstname
	 * @param lastname
	 * @param emailAddress
	 */
	public User(String username, String nickname, String password,
			String firstname, String lastname, String emailAddress) {
		this();
		this.setName( username );
		this.setDescription( nickname );
		this.setPassword( password );
		this.setFirstname( firstname );
		this.setLastname( lastname );
		this.setEmailAddress( emailAddress );
	}
	
	public Boolean hasRole( Role role ){
		return this.getRoles().contains( role );
	}
	
	public Boolean hasRole( String roleName ){
		for( Role role : this.getRoles()){
			if( role.getName().equals( roleName ) ){
				return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}
	
	public Boolean isMemberOf( Group group ){
		return this.getGroups().contains( group );
	}
	
	public Boolean isMemberOf( String groupName ){
		log.debug("Testing if user is member of group name " + groupName );
		if ( groups != null ) {
			for (Group group : groups) {
				if (group.getName().equals(groupName)) {
					return Boolean.TRUE;
				}
			}
		}
		return Boolean.FALSE;
	}
	
	/**
	 * Simplified check for admin users which can be called by .jsp's.
	 * @return
	 */
	public Boolean getIsAdmininstrator(){
		return this.isMemberOf( Constants.SYSTEM_GROUP_ADMINISTRATORS );
	}
	 
	/**
	 * Simplified check for Anonymous user which can be called by .jsp's.
	 * @return
	 */
	public Boolean getIsAnonymous(){
		return this.isMemberOf( Constants.SYSTEM_GROUP_ANONYMOUS );
	}
	
	/**
	 * Creates a User to represent Guest (un-logged-in user). IMPORTANT: This user
	 * is assigned to a group named "Anonymous" which is NOT persisted in the db.
	 * @return
	 */
	public static User getAnonymousUser(){
		User user = new User( "anonymous", "Anonymous/Guest", "",
			"", "", "" );
		user.setGroups( new ArrayList<Group>() );
		user.getGroups().add( new Group("Anonymous", "Guest User") );
		return user;
	}
	
	////////////////////////////////////
	// Getters and Setters
	////////////////////////////////////
	/**
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the description
	 */
	public String getDescription() {
		return description;
	}

	/**
	 * @param description the description to set
	 */
	public void setDescription(String description) {
		this.description = description;
	}
	
	/**
	 * @return nickname is an alias for super.name
	 */
	public String getNickname() {
		return demographic.getFirstname() + ' ' + demographic.getLastname();
	}

	/**
	 * @return the specialCode
	 */
	public String getSpecialCode() {
		return specialCode;
	}

	/**
	 * @param specialCode the specialCode to set
	 */
	public void setSpecialCode(String specialCode) {
		this.specialCode = specialCode;
	}

	/**
	 * @return the statusCode
	 */
	public String getStatusCode() {
		return statusCode;
	}

	/**
	 * @param statusCode the statusCode to set
	 */
	public void setStatusCode(String statusCode) {
		this.statusCode = statusCode;
	}

	/**
	 * @return the creationDate
	 */
	public Date getCreationDate() {
		return creationDate;
	}

	/**
	 * @param creationDate the creationDate to set
	 */
	public void setCreationDate(Date creationDate) {
		this.creationDate = creationDate;
	}

	/**
	 * @return the renewalDate
	 */
	public Date getRenewalDate() {
		return renewalDate;
	}

	/**
	 * @param renewalDate the renewalDate to set
	 */
	public void setRenewalDate(Date renewalDate) {
		this.renewalDate = renewalDate;
	}

	/**
	 * @return username is an alias for super.name
	 */
	public String getUsername() {
		return getName();
	}
	
	/**
	 * username is an alias for super.name
	 */
	public void setUsername( String username ) {
		setName( username );
	}
	
	/**
	 * nickname is an alias for super.name
	 */
	public void setNickname( String nickname ) {
		setDescription( nickname );
	}
	
	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @return the firstname
	 */
	public String getFirstname() {
		return firstname;
	}

	/**
	 * @param firstname the firstname to set
	 */
	public void setFirstname(String firstname) {
		this.getDemographic().setFirstname( firstname );
		this.firstname = firstname;
	}

	/**
	 * @return the lastname
	 */
	public String getLastname() {
		return lastname;
	}

	/**
	 * @param lastname the lastname to set
	 */
	public void setLastname(String lastname) {
		this.getDemographic().setLastname( lastname );
		this.lastname = lastname;
	}

	/**
	 * @return the emailAddress
	 */
	public String getEmailAddress() {
		return emailAddress;
	}

	/**
	 * @param emailAddress the emailAddress to set
	 */
	public void setEmailAddress(String emailAddress) {
		this.getDemographic().setEmailAddress( emailAddress );

		this.emailAddress = emailAddress;
	}

	/**
	 * @return the demographic
	 */
	public Demographic getDemographic() {
		return this.demographic;
	}

	/**
	 * @param demographic the demographic to set
	 */
	public void setDemographic(Demographic demographic) {
		this.demographic = demographic;
	}

	/**
	 * @return the groupKeyList
	 */
	public Set<Key> getGroupKeySet() {
		return groupKeySet;
	}

	/**
	 * @param groupKeyList the groupIdList to set
	 */
	public void setGroupKeySet(Set<Key> groupKeySet) {
		this.groupKeySet = groupKeySet;
	}
	
	/**
	 * @return the groups
	 */
	public List<Group> getGroups() {
		return groups;
	}

	/**
	 * @param groups the groups to set
	 */
	public void setGroups(List<Group> groups) {
		this.groups = groups;
	}

	/**
	 * @return the roleKeyList
	 */
	public Set<Key> getRoleKeySet() {
		return roleKeySet;
	}
	
	/**
	 * @param roleKeyList the groupIdList to set
	 */
	public void setRoleKeySet(Set<Key> roleKeySet) {
		this.roleKeySet = roleKeySet;
	}

	/**
	 * @return the roles
	 */
	public List<Role> getRoles() {
		return roles;
	}

	/**
	 * @param roles the roles to set
	 */
	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}

	/* (non-Javadoc)
	 * @see com.sebscape.sebcms.persistence.domain.IUpdateable#update(java.lang.Object)
	 */
	@Override
	public void update(User newValues) {
		this.setName( newValues.getUsername() );
		this.setDescription( newValues.getNickname() );
		if( !Utils.isEmpty( newValues.getPassword() ) ){
			this.setPassword( newValues.getPassword() );
		}
		this.setFirstname( newValues.getFirstname() );
		this.setLastname( newValues.getLastname() );
		this.setEmailAddress( newValues.getEmailAddress() );
		// groups and roles and demographics.
		this.setGroupKeySet( newValues.getGroupKeySet() );
		this.setRoleKeySet( newValues.getRoleKeySet() );
		log.debug( "Old: " + getDemographic().getAddress1() + ", New: " + newValues.getDemographic().getAddress1());
		this.getDemographic().update( newValues.getDemographic() );
	}

	/* (non-Javadoc)
	 * @see com.sebscape.sebcms.persistence.domain.ICrudPermissions#canCreate(java.lang.String)
	 */
	@Override
	public boolean canCreate(String specialCode) {
		return this.specialCode == null 
			|| this.specialCode.trim().equals( "" )
			|| this.specialCode.toUpperCase().contains( "C" );
	}

	/* (non-Javadoc)
	 * @see com.sebscape.sebcms.persistence.domain.ICrudPermissions#canDelete(java.lang.String)
	 */
	@Override
	public boolean canDelete(String specialCode) {
		return this.specialCode == null 
		|| this.specialCode.trim().equals( "" )
		|| this.specialCode.toUpperCase().contains( "D" );
	}

	/* (non-Javadoc)
	 * @see com.sebscape.sebcms.persistence.domain.ICrudPermissions#canRead(java.lang.String)
	 */
	@Override
	public boolean canRead(String specialCode) {
		return this.specialCode == null 
		|| this.specialCode.trim().equals( "" )
		|| this.specialCode.toUpperCase().contains( "R" );
	}

	/* (non-Javadoc)
	 * @see com.sebscape.sebcms.persistence.domain.ICrudPermissions#canUpdate(java.lang.String)
	 */
	@Override
	public boolean canUpdate(String specialCode) {
		return this.specialCode == null 
		|| this.specialCode.trim().equals( "" )
		|| this.specialCode.toUpperCase().contains( "U" );
	}

	
}
